﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace Sunny.Lib
{
    public class CertificateHelper
    {
        public static X509Certificate2Collection GetCertificates(string keyValue, X509FindType findType = X509FindType.FindBySubjectName, StoreName storeName = StoreName.My, StoreLocation storeLocation = StoreLocation.LocalMachine)
        {
            X509Certificate2Collection foundCert = null;

            X509Store store = new X509Store(storeName, storeLocation);
            store.Open(OpenFlags.MaxAllowed);

            var x509CertCollection = store.Certificates.Find(findType, keyValue, true);
            foundCert = x509CertCollection;

            return foundCert;
        }

        public static X509Certificate GetCertificate(string keyValue, X509FindType findType = X509FindType.FindBySubjectName, StoreName storeName = StoreName.My, StoreLocation storeLocation = StoreLocation.LocalMachine)
        {
            X509Certificate cert = null;

            X509Certificate2Collection certs = GetCertificates(keyValue, findType, storeName, storeLocation);
            if (certs.Count > 1)
            {
                cert = certs[0];
            }

            return cert;
        }

        public static X509Certificate2 GetCertificate(string certPath, string password)
        {
            X509Certificate2 cert = new X509Certificate2(EnvironmentHelper.ExpandEnvironmentVariables(certPath), password);
            return cert;
        }

        public static X509Certificate GetCertificate1(string certPath, string password)
        {
            X509Certificate cert = new X509Certificate(EnvironmentHelper.ExpandEnvironmentVariables(certPath), password);
            return cert;
        }

        public static void InsertCertificate(string certFile, StoreName storeName = StoreName.My, StoreLocation storeLocation = StoreLocation.LocalMachine)
        {
            X509Certificate2 cert = new X509Certificate2(certFile);

            // Must install the self-signed certificate into root as well to make it be trusted.
            X509Store store = new X509Store(StoreName.Root, storeLocation);
            store.Open(OpenFlags.ReadWrite);
            store.Add(cert);
            store.Close();

            store = new X509Store(storeName, storeLocation);
            store.Open(OpenFlags.ReadWrite);
            store.Add(cert);
            store.Close();

            // It takes long time to verify the cert when first installed on a clean machine. This will cause an decryption failure if decrypt immeditately.
            int retry = 0;
            while (!VerifyCert(cert) && retry++ < 6)
            {
                Thread.Sleep(5000);
            };
        }

        private static bool VerifyCert(X509Certificate2 cert)
        {
            bool result = false;

            try
            {
                result = cert.Verify();
            }
            catch (Exception e)
            {
                EventInfo myEvent = new EventInfo(EventLevel.Error, DateTime.Now);
                myEvent.StackTrace = typeof(CertificateHelper).FullName;
                myEvent.Details = e.Message;

                Logger.LogEvent(myEvent);
            }

            return result;
        }
    }
}
